#pragma once
#include <cmath>
#include <limits>

namespace Putin{

/**
 * Vector 2
 */
template <typename T>
class Vec2
{
public:

	T x; /** X Coord*/
	T y; /** Y Coord*/

	/**
	 * Initialize the vector with 0
	 */
	Vec2()
	{
		x = y = 0;
	}

	/**
	 * Initialize the vector with parameters
	 * @param x the x val
	 * @param y the y val
	 */
	Vec2(T x,T y)
	{
		this->x = x;
		this->y = y;
	}

	/**
	 * Multiplication with a scalar
	 */
	Vec2<T> operator*(T t) const
	{
		Vec2<T> v(x*t ,y*t);
		return v;
	}

	/**
	 * Addition
	 */
	Vec2<T> operator+(const Vec2 v)const
	{
		Vec2<T> v(x+v.x,y+v.y);
		return v;
	}

	/**
	 * Subtraction
	 */
	Vec2<T> operator-(const Vec2 v)const
	{
		Vec2<T> v(x-v.x, y-v.y);
		return v;
	}

	/**
	 * Unary subtraction
	 */
	Vec2<T> operator-(void) const
	{
		Vec2<T> v(-x,-y);
	}

};

template <typename T>
class Vec3
{
public:

	// nice, non standard, ugly hack to make it easy to derive the c-array
	union{
		struct{
			T x; /** X Coord*/
			T y; /** Y Coord*/
			T z; /** Z coord */
		};
		float v[3];
	};

	T operator[](int i)
	{
		assert( i >= 0 && i <= 2 );
		return v[i];
	}

	/**
	 * Initialize the vector with 0
	 */
	Vec3()
	{
		x = y = z = 0;
	}

	/**
	 * Initialize the vector with parameters
	 * @param x the x val
	 * @param y the y val
	 */
	Vec3(T x,T y, T z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}

	/**
	 * Multiplication with a scalar
	 */
	Vec3<T> operator*(T t) const
	{
		Vec3<T> v(x*t ,y*t, z*t);
		return v;
	}

	/**
	 * Elemente wise vector multiplication
	 */
	Vec3<T> operator|(const Vec3<T> t) const
	{
		return Vec3<T>( x*t.x, y*t.y, z*t.z );
	}

	/*!
	 * \brief Dot product
	*/
	T operator*(const Vec3<T> t) const
	{
		return x*t.x + y*t.y + z*t.x;
	}

	Vec3<T> operator/(const Vec3<T> t) const
	{
		return Vec3<T>( x/t.x, y/t.y, z/t.z );
	}

	Vec3<T> operator/(const T h) const
	{
		return Vec3<T>( x/h, y/h, z/h );
	}

	Vec3<T> operator^(const Vec3<T> t)const
	{
		return Vec3<T>( y*t.z - z * t.y,
						z*t.x - x * t.z,
						x*t.y - y * t.x);
	}

	/**
	 * Addition
	 */
	Vec3<T> operator+(const Vec3 v)const
	{
		return Vec3<T>(x+v.x,y+v.y,z+v.z);
	}

	bool operator==(const Vec3& v) const
	{
		const T e = (T) 0.00001; // small range to test
		return  ( (T) fabs(x-v.x) < e ) &&
				( (T) fabs(y-v.y) < e ) &&
				( (T) fabs(z-v.z) < e );
	}

	bool operator!=(const Vec3&v) const
	{
		return !((*this) == v);
	}

	bool isInf() const
	{
		// DOES NOT WORK
		T inf = std::numeric_limits<T>::infinity();
		return  x == inf || y == inf || z == inf;
	}
	/**
	 * Subtraction
	 */
	Vec3<T> operator-(const Vec3 v)const
	{
		return Vec3<T>(x-v.x, y-v.y, z-v.z);
	}

	/**
	 * Unary subtraction
	 */
	Vec3<T> operator-(void) const
	{
		Vec3<T> v(-x,-y,-z);
	}


	T length() const
	{
		return sqrt( x*x + y*y + z*z); 
	}

	Vec3<T> normalized() const
	{
		T l = length();
		return Vec3<T>( x / l, y / l, z/ l); 
	}
};

typedef Vec2<float> Vec2f;
typedef Vec2<int> Vec2i;
typedef Vec3<float> Vec3f;
typedef Vec3<int> Vec3i;
}
